package main;

import java.util.HashMap;
import java.util.Map;

import main.Neuron.Input;

public class BackPropagation {

	NeuralNetwork network;

	Map<Neuron, Double> errors;

	public BackPropagation(NeuralNetwork network) {
		this.network = network;
	}

	public void addData(Data data, double learningRate){
		network.reset();
		errors = new HashMap<Neuron, Double>();

		for (InputNeuron n: network.inputNeurons){
			n.setValue(data.ins.get(n));
		}

		for (Neuron n: network.outputNeurons){
			double val = n.getValue();

			double expected = data.outs.get(n);
			//Threshold neuron , so error should be it
			double error = (expected-val);
			for (Input n1: n.ins){
				n1.setWeight(n1.getWeight() + n1.getNeuron().getValue()*learningRate*error);
			}
			errors.put(n, error);
		}

		for (int i=network.hiddenNeurons.size()-1; i>=0; i--){
			for (Neuron n: network.hiddenNeurons.get(i)){
				double val = n.getValue();
				double error = 0;
				for (Neuron n1: n.outs){
					double w = n1.getWeight(n);
					double n1err = errors.get(n1);
					error = error + w*n1err;
				}
				error = error * val * (1-val);

				for (Input n1: n.ins){
					n1.setWeight(n1.getWeight() + n1.getNeuron().getValue()*learningRate*error);
				}
				errors.put(n, error);
			}
		}

	}

	public static class Data{
		Map<Neuron, Double> outs;
		Map<Neuron, Double> ins;

		public Data(Map<Neuron, Double> ins, Map<Neuron, Double> outs) {
			this.outs = outs;
			this.ins = ins;
		}
		
		@Override
		public String toString() {
			return outs.toString() + "  " + ins.toString();
		}
	}
}
